
import ij.IJ;
import ij.ImagePlus;
import ij.process.ImageProcessor;
import ij.text.TextPanel;
import ij.text.TextWindow;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 *
 * @author Michalis Dobekidis
 */
public class WindowScanner
{
    public ImageProcessor newImg;
    public int[] totals;
    TextPanel logPanel = new TextPanel();
    TextWindow textLog;
    private BufferedWriter out;


    public WindowScanner()
    {
        //nothing
    }
    
    /**
     *
     * @param imageObject the Image data
     * @param width of the sliding window
     * @param height of the sliding window
     * @param stepX how much the sliding window moves on the xx' axis
     * @param stepY how much the sliding window move on the yy' axis
     * @param roi the rectancle area we might have as an indication of the window parameters
     * @return returns a two dimentional array that contains various two dimentional arrays that represent sliding window instances
     *
     * Receives an image and starts producing sliding windows over the pixels of that image, then returns the array of arrays.
     * used a multidimentional array for easy access and indentification of individual pixels and their position in a particular window.
     */
    public int[][][][] slidingWindow(ImageProcessor imageObject , int width , int height , int stepX , int stepY,Rectangle roi)
        {
            int totalHeight =  roi.height!=0?roi.height-height:(imageObject.getHeight()-height);
            int totalWidth  =  roi.width!=0?roi.width-width:(imageObject.getWidth()-width);
            int[][] temp;
            int roiHeight = height;
            int roiWidth  = width;
            int counter = 0;
            int externalCounter = 0;
           newImg = imageObject.createProcessor(imageObject.getWidth(), imageObject.getHeight()); 
        
           totals = getRepeats(totalWidth,totalHeight,stepX,stepY);
           
           int[][][][] featureArray = new int[totals[0]][totals[1]][][];
           
           int i;
           int j;
      
            for(i = roi.x; i<=totalHeight; i+=(stepY))
            {
                for(j = roi.y; j<=totalWidth; j+=(stepX))
                {
                   temp = getVector(i,j,imageObject,roiHeight,roiWidth);
                   featureArray[externalCounter][counter] = temp;
                   counter++;
                }
                counter = 0;
               externalCounter++;
            }
               
         
       
             return featureArray;
        }

    /*
     * Calculates the elements of the vector, and retrives their pixels.
     *
     * @param external columns
     * @param internal rows
     * @param imp actual image object
     * @param height of the box
     * @param width of the box
     * @return an array of Integers (pixels)
     */
    public int[][] getVector(int external, int internal, ImageProcessor imp,int height,int width)
    {
        int[][] outputArray = new int[height][width];
        int counter = 0;
        int ext=external;
        int inte=internal;
        int externalCounter = 0;
                
        for(int i=ext;i<external+height;i++)
        {
            for(int j=inte;j<internal+width;j++)
            {
                outputArray[externalCounter][counter] = imp.getPixel(i, j);
                counter++;
            }
          
            counter = 0;
            externalCounter++;
        }
        return outputArray;
    }



    /**
     *
     * @param pixelArray the array of pixels
     * @param totalValues total values that are contained in the pixel array
     * @param windowWidth the sliding window width
     * @param windowHeight the sliding window height
     * @param totalWidth total width of the image
     * @param totalHeight total height of the image
     *
     *
     * when all repeats end a new image is created from the original pixels
     */
    public void reconstructImage(int[][][][] pixelArray,int windowWidth, int windowHeight,int totalWidth,int totalHeight,int stepX, int stepY,int imageWidth,int imageHeight)
    {
          int[] newArray = new int[((totals[0]*totals[1])*(windowHeight*windowWidth))+1];
          int counter = 0;
          
          //Reconstruction happens on the fly here.
            for(int i=0;i<totalHeight;i++)
            {
            for(int j=0;j<totalWidth;j++)
            {
              for(int k=0;k<windowHeight;k++)
              {
                  for(int f=0;f<windowWidth;f++)
                  {
                     newArray[counter] =  pixelArray[i][j][k][f];
                     counter++;
                  }

              }
            }

            }

         applyReconstruct(newArray,windowWidth,windowHeight,imageWidth,imageHeight,stepX,stepY);
         ImagePlus newImagePlus = new ImagePlus("new image",newImg);
         newImagePlus.show();
         newImagePlus.draw();

       }


    private void applyReconstruct(int[] pixels,int boxWidth, int boxHeight,int imageWidth, int imageHeight,int stepX, int stepY)
    {
         int counter = 0;
         for(int i=0;i<=imageHeight-boxHeight;i+=stepY)
              {
                  for(int j=0;j<=imageWidth-boxWidth;j+=stepX)
                  {
                      for(int k=i;k<i+boxHeight;k++)
                      {
                          for(int f=j;f<j+boxWidth;f++)
                          {
                            
                              newImg.putPixel( k, f, pixels[counter]);
                              counter++;
                          }
                      }
                      // IJ.log(pixels[1587600]+"<--"+pixels.length);
                     //  IJ.log(counter+"<--Length"+mean.length);
                  }

              }
    }

    /**
     *
     * @param pixelArray the array of pixels
     * @param totalValues total values that are contained in the pixel array
     * @param windowWidth the sliding window width
     * @param windowHeight the sliding window height
     * @param totalWidth total width of the image
     * @param totalHeight total height of the image
     *
     * calculates the mean of each window and applies in to a new image
     * when all repeats end a new image is created from the modified pixels
     */
    public void calculateMean(int[][][][] pixelArray,int windowWidth, int windowHeight,int totalWidth,int totalHeight,int stepX, int stepY,int imageWidth,int imageHeight)
    {
        int mean;
        int meanResult = 0;
        int c=0;
        int counter = 0;
        int externalCounter = 0;
        int[] numberOfItems = new int[windowHeight*windowWidth];
        int[] meanArray = new int[totalHeight*totalWidth];
        

        
            for(int i=0;i<totalHeight;i++)
            {
            for(int j=0;j<totalWidth;j++)
            {
              for(int k=0;k<windowHeight;k++)
              {
                  for(int f=0;f<windowWidth;f++)
                  {
                      meanResult = meanResult+pixelArray[i][j][k][f];
                     numberOfItems[c++] = meanResult;
                  }
              }
              mean = Math.round(meanResult/numberOfItems.length);
              // initializing variables
              meanResult = 0;
              c = 0;
              numberOfItems= new int[windowHeight*windowWidth];
              meanArray[counter] = mean;
              mean = 0;
              counter++;
              }
            externalCounter++;
            }
           
         applyMean(meanArray,windowWidth,windowHeight,imageWidth,imageHeight,stepX,stepY);
         ImagePlus newImagePlus = new ImagePlus("new image",newImg);
         newImagePlus.show();
         newImagePlus.draw();
         
       }


    /**
     *
     * @param external the column that the sliding window starts
     * @param internal the row that the sliding window starts
     * @param mean the mean of the pixels of the current window
     * @param width the width of the window
     * @param height the height of the window
     *
     * applies mean values on the pixels of a new image
     */
    private void applyMean(int[] mean,int boxWidth, int boxHeight,int imageWidth, int imageHeight,int stepX, int stepY)
    {
        int counter = 0;
         for(int i=0;i<=imageHeight-boxHeight;i+=stepY)
              {
                  for(int j=0;j<=imageWidth-boxWidth;j+=stepX)
                  {
                      for(int k=i;k<=i+boxHeight;k++)
                      {
                          for(int f=j;f<=j+boxWidth;f++)
                          {
                              newImg.putPixel( k, f, mean[counter]);
                          }
                      }
                      counter++;
                  }

              }
    }
    
    /**
     *
     * @param featuresArray the array of pixels to be writen in a file
     * @param fileName the desired filename
     * @param totalWidth the total width of the array
     * @param totalHeight the total height of the array
     * @param windowWidth the width of a window
     * @param windowHeight the height of a window
     * @param directory the desired directory for the file to be writen in
     *
     * goes through all the values of the array and converts them to a StringBuilder in order to make each value unique in the system memory.
     */
    public void saveFeatures(int[][][][] featuresArray, int windowWidth, int windowHeight,int totalWidth,int totalHeight,String dir,boolean localSave)
    {
        String tempStr = "";
        String stringChunk = "";//new String[totalHeight*totalWidth];
        StringBuilder stringBuffer = new StringBuilder();
        int c = 0;
        String defaultDir;
        defaultDir = dir.equals("") ? IJ.getDirectory("imagej")+"vectors.txt" : dir;
        char ch = 0;
        //int[] numberOfItems = new int[((totals[0]*totals[1])*(windowHeight*windowWidth))+1];
        for(int i=0;i<totalHeight;i++)
        {
            for(int j=0;j<totalWidth;j++)
            {
                for(int k=0;k<windowHeight;k++)
                {
                    for(int f=0;f<windowWidth;f++)
                    {
                        stringBuffer.append(featuresArray[i][j][k][f]).append(" ");
                    }
                   
                }
                stringBuffer.append("\n");
            }
        }
        tempStr = stringBuffer.toString();
        stringBuffer = new StringBuilder();

         IJ.log(tempStr);
        

          if(localSave)
            IJ.saveString(tempStr, defaultDir);
            tempStr = new String();
         
    }


    /**
     *
     * @param totalWidth the total width of the image
     * @param totalHeight the total width of the image
     * @param stepX the step with which the window moves to xx' axis
     * @param stepY the step with which the window moves to yy' axis
     * @return returns the total number of repeats, and the total number of windows that will be created
     * 
     */
    private int[] getRepeats(int totalWidth,int totalHeight,int stepX,int stepY)
    {
        int[] totalArr = new int[2];
        int numberOfWindows = 0;
        int numberOfRepeats = 0;
        boolean passOne = false;
        
          for(int i = 0; i<=totalHeight; i+=(stepY))
            {
              if(!passOne)
              {
                for(int j =0 ; j<=totalWidth; j+=(stepX))
                {
                     numberOfWindows++;
                }
               }
               passOne = true;
               numberOfRepeats++;
            }

        
        totalArr[0] = numberOfRepeats;
        totalArr[1] = numberOfWindows;
        
        return totalArr;
    }


//Obsolete, we use IJ.saveString(String, path) now. Kept in case of non-ImageJ testing.
    private void writeOutput(String source,String dir)
    {
        try
        {
           out = new BufferedWriter(new FileWriter(dir,true));

            //String someText = "aString\nthis is a\nttest";
            //someText.replaceAll("\n", System.getProperty("line.separator"));

            out.write(source);
            out.newLine();
            out.flush();
            out.close();
        }
        catch (IOException e)
        {
            IJ.log("Exception "+e);
        }
    }

    /**
     * Reads the file from the system.
     * @return a String with all the values separated by comma.
     */
    private String readLog()
    {
        String filename = "C:/SDK/Vector.txt";

          String records = new String();
          try
          {

            BufferedReader reader = new BufferedReader(new FileReader(filename));
            String line;
            while ((line = reader.readLine()) != null)
            {
              records = records+line;
            }
            reader.close();
            return records;
          }
          catch (Exception e)
          {
            System.err.format("Exception occurred trying to read '%s'.", filename);
            e.printStackTrace();
            return null;
          }

    }

  public static void deleteTemp(String fileName)
  {
    
    // A File object to represent the filename
    File f = new File(fileName);

    // Make sure the file or directory exists and isn't write protected
    if (!f.exists())
      throw new IllegalArgumentException(
          "Delete: no such file or directory: " + fileName);

    if (!f.canWrite())
      throw new IllegalArgumentException("Delete: write protected: "
          + fileName);

    // If it is a directory, make sure it is empty
    if (f.isDirectory()) {
      String[] files = f.list();
      if (files.length > 0)
        throw new IllegalArgumentException(
            "Delete: directory not empty: " + fileName);
    }

    // Attempt to delete it
    boolean success = f.delete();

    if (!success)
      throw new IllegalArgumentException("Delete: deletion failed");
  }



}
